/*
 * VisualizerHTML.java
 *
 * Created on 21 February 2005, 19:56
 */

package org.datamining.guha.utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

//import java.awt.Paint;
import java.awt.Color;

import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.xy.*;
//import org.jfree.data.*;
import org.jfree.data.xy.*;
//import org.jfree.data.category.*;

import org.datamining.guha.association.AbstractAssociation;
import org.datamining.guha.model.*;
import org.datamining.guha.model.literal.*;
import org.datamining.guha.model.node.Node;

import org.datamining.guha.rule.*;

import org.apache.commons.logging.*;


/**
 * This class provides support for visualization
 * of mining results. It generates HTML file.
 * 
 * @author Lukas Vlcek
 */
public class VisualizerHTML {
    
    private static String HTMLFILE = "guha.html";
    private static String MODELFILE = "model.html";
    private static String RULESFILE = "rules.html";
    private static String FRCHARTFILE = "frequencies.jpg";
    //private static String FRCHARTFILESVG = "frequencies.svg";
    private static String GRAPHXMLFILE = "graph.xml";
    private static Log log = LogFactory.getLog(VisualizerHTML.class);
    
    private static int rows = 0;
    
    /** Creates a new instance of VisualizerHTML */
    private VisualizerHTML() {
    }
    
    private static File getFile(String pathname) {
        File file = new File(pathname);
        return file;
    }
    
    private static void createTable(FileWriter fw, PatternTreeModel ptm)
    throws IOException {
        Node root = ptm.getModelRoot();
        if (root == null) { fw.write("No data in model!"); return; }
        
        fw.write("<table border='1' cellpadding=2 cellspacing=0>");
        generateTable(fw, root);
        fw.write("</table>");
        fw.write("<p>Total number of distinct combinations in this tree: " + rows);
        
    }
    
    private static void generateTable(FileWriter fw, Node n)
    throws IOException {
        
        if (n.isRoot()) { fw.write("<tr>"); }
        fw.write("<td align='right'>" + n + "</td>");
        for (int i = 0; i < n.getSuccessors().size(); i++) {
            Node child = (Node) n.getSuccessors().get(i);
            if (i > 0) {
                generateTableRowBeginning(fw, child.getDepth());
            }

            generateTable(fw, child);
            
            if (child.getSuccessors().isEmpty()) {
                fw.write("<td>[" + child.getFrequency() + "]</td></tr>");
                rows += 1;
            }
        }
    }
    
    private static void generateTableRowBeginning(FileWriter fw, int num)
    throws IOException {
        fw.write("<tr>");
        for (int i = 0; i < num; i++) {
            fw.write("<td>&nbsp;</td>");
        }
    }
    
    /**
     * Create xml file on filesystem in given folder from given root Node.
     * This file can be visualized by third party tools (like excellent Prefuse).
     */
    public static void generateXml(String pathname, Node root) {
        
        File file = getFile(pathname + VisualizerHTML.GRAPHXMLFILE);
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            
            fw.write("<graph directed='0'>\n");
            generateXml(fw, root);
            fw.write("</graph>\n");
            
        } catch (IOException ex) {
            log.error(ex);
        } finally {
            if (fw != null) { 
                try { fw.close(); }
                catch (IOException ex) { }
            }
        }
    }
    
    /**
     * Generate xml file.
     */
    private static void generateXml(FileWriter fw, Node n)
    throws IOException {
        
        fw.write(nodeToXml(n, 1) + "\n");
        for (int i = 0; i < n.getSuccessors().size(); i++) {
            Node child = (Node) n.getSuccessors().get(i);
            
            generateXml(fw, child);
            
            int frequency = (int)((child.getFrequency()/15));
            if (frequency < 1) {
            	frequency = 1;
            }
            
            fw.write("<edge label=''" +
                     " source='" + n.hashCode() +
                     "' target='" + child.hashCode() +
                     "' weight='" + frequency +
                     "'></edge>\n");
        }
    }
    
    /**
     * Generate xml representation of given node.
     * Given int is used for node id.
     */
    private static String nodeToXml(Node n, int counter) {
        return "<node id='" + n.hashCode() +
               "' label='" + n +
               "'></node>";
    }
    
    /** Reload button including form tag. */
    private static String reloadButton() {
        return "\n<form>" +
               "<input type='button' name='reload' value='Reload' " +
               "onClick='javascript:document.location.reload()'>" +
               "</form>\n";
    }
    
    /**
     * Header on the top og page including reload button.
     * @param caption Text to appead in header netx to button.
     */
    private static String header(String caption) {
        return "<table>" +
                "<tr><td>"+reloadButton()+"</td>" +
                "<td><h2>"+caption+"</h2></td></tr>" +
                "</table>";
    }
    
    /**
     * Generate html document which contains frames for
     * all specific html reports.
     */
    public static void generateHTML(String pathname) {
        File file = getFile(pathname + VisualizerHTML.HTMLFILE);
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            fw.write("<html><frameset rows='75%,25%'>" +
                    "<frame name='model' src='" + MODELFILE + "'>" +
                    "</frame>" +
                    "<frame name='rules' src='" + RULESFILE + "'>" +
                    "</frame>" +
                    "</frameset></html>"
                    );
        } catch (IOException ex) {
            log.error(ex);
        } finally {
            if (fw != null) { 
                try { fw.close(); }
                catch (IOException ex) { }
            }
        }
    }
    
    /**
     * Generate html document with rules.
     */
    public static void generateRules(String pathname, AbstractAssociation assoc, List rules) {
        File file = getFile(pathname + VisualizerHTML.RULESFILE);
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            fw.write("<html><body>" + header(":: Rules report"));
            
            fw.write(rules.size() + " rules in total.");
            
            fw.write("<pre>");
            fw.write(assoc.toString());
            fw.write("</pre>");
            
            fw.write("Antecedents:");
            fw.write("<pre>");
            for (int a = 0; a < assoc.getAntecedents().size(); a++) {
                Item item = (Item) assoc.getAntecedents().get(a);
                fw.write(item.getName() + " ");
                String delimiter = "";
                for (int i = 0; i < item.getIntervalNames().length; i ++) {
                    fw.write(delimiter + item.getIntervalNames()[i]);
                    delimiter = ", ";
                }
                fw.write("\n");
            }
            fw.write("</pre>");
            
            fw.write("Succedents:");
            fw.write("<pre>");
            for (int a = 0; a < assoc.getSuccedents().size(); a++) {
                Item item = (Item) assoc.getSuccedents().get(a);
                fw.write(item.getName() + " ");
                String delimiter = "";
                for (int i = 0; i < item.getIntervalNames().length; i ++) {
                    fw.write(delimiter + item.getIntervalNames()[i]);
                    delimiter = ", ";
                }
                fw.write("\n");
            }
            fw.write("</pre>");
            
            if (rules.size() > 0) {
                fw.write("<table>");
                fw.write("<tr><td><b>Antecedent</b></td><td align='center'>~</td><td><b>Succedent</b></td>" +
                        "<td><b>Support</b></td><td><b>Confidence</b></td></tr>");
                    for (int r = 0; r < rules.size(); r++) {
                        Rule rule = (Rule) rules.get(r);
                        fw.write(rule.toHTML());
                    }
                fw.write("</table>");
            }
            fw.write("</body></html>");
        } catch (IOException ex) {
            log.error(ex);
        } finally {
            if (fw != null) { 
                try { fw.close(); }
                catch (IOException ex) { }
            }
        }
    }
    
    /**
     * Generate html document with model details.
     */
    public static void generateModel(String pathname, PatternTreeModel ptm, int numberOfObjects, ModelAdapter adapter) {
        
        File file = getFile(pathname + VisualizerHTML.MODELFILE);
        FileWriter fw = null;
        try {
            fw = new FileWriter(file);
            fw.write("<html><body>" + header(":: Model details"));
            
            /*
            fw.write("<pre>");
            fw.write(ptm.toString());
            fw.write("</pre>");
             */
            
            generateFrequencyChart(pathname, ptm.getFrList(), numberOfObjects, adapter);
            fw.write("\n<img border=0 src=\"" + FRCHARTFILE + "\"/>\n");
            
            fw.write("<p>Frequencies: ");
            fw.write(ptm.getFrList().toString());
            fw.write("</p>");
            
            createTable(fw, ptm);
            /*
            fw.write("<pre>");
            fw.write(ptm.toStringLiteralNodeList());
            fw.write("</pre>");
            */
            fw.write("</body></html>");
            fw.flush();
        } catch (IOException ex) {
            log.error(ex);
        } finally {
            if (fw != null) { 
                try { fw.close(); }
                catch (IOException ex) { }
            }
        }
    }
    
    /**
     * Generate chart of Model Literal Frequencies.
     */
    private static void generateFrequencyChart(String pathname, FrequencyOrderedList frList, int numberOfObjects, ModelAdapter adapter) {
        List list = frList.getLiteralList();
        
        XYSeries seriesAnte = new XYSeries("Antecedent Frequencies");
        XYSeries seriesSucc = new XYSeries("Succedent Frequencies");
        //XYSeries mean = new XYSeries("Uniform distribution");
        
        //int pctl = numberOfObjects/2;
        int size = list.size();
        String[] symbols = new String[size];
        
        for (int i = 0; i < size; i++) {
            Literal lit = (Literal) list.get(i);
            symbols[i] = lit.toString();
            long supp = 0;
            
            if (adapter.isAntecedent(lit)) {
                supp = frList.getLiteralSupport(lit);
                seriesAnte.add((i), supp);
                seriesSucc.add((i), 0);
            } else if(adapter.isSuccedent(lit)) {
                supp = frList.getLiteralSupport(lit);
                seriesAnte.add((i), 0);
                seriesSucc.add((i), supp);
            }
            //mean.add((i), pctl);
        }
        
        XYDataset dataset1 = new XYSeriesCollection(seriesAnte);
        XYDataset dataset2 = new XYSeriesCollection(seriesSucc);
        //XYDataset dataset3 = new XYSeriesCollection(mean);
        
        XYItemRenderer renderer1 = new XYBarRenderer();
        XYItemRenderer renderer2 = new XYBarRenderer();
        //XYItemRenderer renderer3 = new StandardXYItemRenderer();
        
        ValueAxis rangeAxis = new NumberAxis("Frequency");
        ValueAxis domainAxis = new SymbolicAxis("Properties", symbols);
        //domainAxis.setVerticalTickLabels(true);
        
        XYPlot plot = new XYPlot(dataset1, domainAxis, rangeAxis, renderer1);
        
        plot.setDataset(0, dataset1);
        plot.setDataset(1, dataset2);
        //plot.setDataset(2, dataset3);
        
        plot.setRenderer(0, renderer1);
        plot.setRenderer(1, renderer2);
        //plot.setRenderer(2, renderer3);
        
        renderer1.setPaint(Color.decode("#0000FF"));
        renderer2.setPaint(Color.decode("#00BB00"));
        //renderer3.setPaint(Color.decode("#FF0000"));
                
        JFreeChart chart = new JFreeChart("Frequencies of ordered properties",
                JFreeChart.DEFAULT_TITLE_FONT, plot, false);
        
        chart.getXYPlot().setForegroundAlpha(0.60f);
        chart.getXYPlot().setOrientation(PlotOrientation.VERTICAL);

        String filePath;
        filePath = pathname + VisualizerHTML.FRCHARTFILE;
        try {
        	log.debug("Writing " + filePath);
            ChartUtilities.saveChartAsJPEG(
                new java.io.File(filePath), 1, chart, 500, 300
                );
        } catch (IOException ex) {
            log.error(ex);
        }
        
        // Prepare BW chart for SVG output.
        /*
        filePath = pathname + VisualizerHTML.FRCHARTFILESVG;
        try {
        	
        	chart.getXYPlot().setForegroundAlpha(1.0f);
        	chart.setBackgroundPaint(Color.decode("#FFFFFF"));
        	chart.setBorderPaint(Color.decode("#000000"));
        	chart.setBorderVisible(true);
        	
        	chart.getXYPlot().getRenderer(0).setPaint(Color.decode("#000000"));
        	chart.getXYPlot().getRenderer(1).setPaint(Color.decode("#000000"));
        	//chart.getXYPlot().getRenderer(2).setPaint(Color.decode("#000000"));
        	
        	chart.getXYPlot().getRangeAxis().setUpperBound(200);
        	
        	MyChartUtils util = new MyChartUtils();
        	log.debug("Writing " + filePath);
        	util.saveChartToSVG(chart, filePath, 500, 300);
        } catch (Exception ex) {
            log.error(ex);
        }
        */
        
    }
}
